from strategylib.models.bbc_oms_enum import OrderStatus

from vn_local.app.market_maker import CtaEngine
from vn_local.app.market_maker.template import CtaTemplate
from vn_local.app.market_maker.base import StopOrder
from vn_local.trader.constant import ApiSource, EmptyList
from vn_local.trader.object import (
    TickData,
    BarData,
    OrderTradeData,
    AccountData,
    PublicTradeData, SubscribeRequest, Exchange, Direction, Status, PfitLog, OrderType, QueryOpenOrderRequest, Product)

from vn_local.trader.utility import load_json, round_to, DotDict, extract_vt_symbol
import numpy as np
from scipy.stats import t
from typing import List, Dict, Tuple
import time
from datetime import datetime
from collections import deque
import random


def sort_dict(d, reverse=True):
    return sorted(d.items(), key=lambda item: item[0][0], reverse=reverse)


def generate_existing_target_books(existing_order_orderid_dict, paper_book):
    existing_order_in_book = set(existing_order_orderid_dict.keys()).intersection(paper_book)

    target_book = [value for value in paper_book if value not in existing_order_in_book]
    existing_book = {key: value for key, value in existing_order_orderid_dict.items() if
                     key not in existing_order_in_book}

    return target_book, existing_book


def find_differences(existing_orderid_order_dict, paper_book):
    # 将 existing_orderid_order_dict 的值转换为集合以便快速查找
    existing_values_set = set(existing_orderid_order_dict.values())

    # 找到 paper_book 中存在但不在 existing_orderid_order_dict 中的项/ orders_to_insert
    missing_in_dict = [item for item in paper_book if item not in existing_values_set]

    # 找到 existing_orderid_order_dict 中存在但不在 paper_book 中的项，并记录其键值对 / orders_to_cancel
    missing_in_book = {key: value for key, value in existing_orderid_order_dict.items() if value not in set(paper_book)}

    return missing_in_dict, missing_in_book


def find_key_by_value(d, target_value):
    """
    在字典中查找对应于指定值的键。

    参数:
    d (dict): 要查找的字典。
    target_value: 要查找的值。

    返回:
    key: 对应于目标值的键。如果没有找到对应的键，返回 None。
    """
    for key, value in d.items():
        if value == target_value:
            return key
    return None


def split_list_by_value(data, target_value, equal_apply_to_greater=True):
    # 如果输入是字典
    if isinstance(data, dict):
        if equal_apply_to_greater:
            greater_than_value = {key: value for key, value in data.items() if value[0] >= target_value}
            less_than_value = {key: value for key, value in data.items() if value[0] < target_value}
        else:
            greater_than_value = {key: value for key, value in data.items() if value[0] > target_value}
            less_than_value = {key: value for key, value in data.items() if value[0] <= target_value}

    # 如果输入是列表
    elif isinstance(data, list):
        if equal_apply_to_greater:
            greater_than_value = [item for item in data if item[0] >= target_value]
            less_than_value = [item for item in data if item[0] < target_value]
        else:
            greater_than_value = [item for item in data if item[0] > target_value]
            less_than_value = [item for item in data if item[0] <= target_value]

    # 如果输入既不是字典也不是列表，抛出异常
    else:
        raise ValueError("Input must be either a dict or a list")

    return greater_than_value, less_than_value


def generate_batch(existing_order_orderid_dict, paper_book, side="buy"):
    # print("existing_order_orderid_dict: ", existing_order_orderid_dict)
    # print(paper_book)
    # define trend
    if existing_order_orderid_dict:
        existing_extreme_value = min(existing_order_orderid_dict.keys(), key=lambda x: x[0])[0] if side == "sell" else \
            max(existing_order_orderid_dict.keys(), key=lambda x: x[0])[0]
    else:
        existing_extreme_value = 0

    paper_extreme_value = min(paper_book, key=lambda x: x[0])[0] if side == "sell" else \
        max(paper_book, key=lambda x: x[0])[0]

    trend = 0
    if existing_extreme_value > paper_extreme_value:
        trend = -1
    else:
        trend = 1

    # filter existing order
    target_book, existing_book = generate_existing_target_books(existing_order_orderid_dict, paper_book)

    # sort prices
    reverse = True if trend == -1 else False
    sorted_existing_book = sort_dict(existing_book, reverse=reverse)
    sorted_target_book = sorted(target_book, key=lambda x: x[0], reverse=reverse)

    cancel_orders = sorted_existing_book
    insert_orders = sorted_target_book

    return cancel_orders, insert_orders, trend


def ensure_min_spacing(existing_list, min_spacing, ascending):
    if not existing_list:
        return []

    result = [existing_list[0]]
    if ascending:
        for i in range(1, len(existing_list)):
            next_value = max(existing_list[i], result[-1] + min_spacing)
            result.append(next_value)
    else:
        for i in range(1, len(existing_list)):
            next_value = min(existing_list[i], result[-1] - min_spacing)
            result.append(next_value)

    return result


def generate_layer_orderbook(referred_price, price_limit, covered_depth, covered_liquidity, pricetick=0.0001,
                             lot_size=0.0005, min_volume=10, ascending=True):
    even_intervals = np.linspace(referred_price, price_limit, covered_depth + 1)

    prices = []

    for i in range(len(even_intervals) - 1):
        price = np.random.uniform(even_intervals[i], even_intervals[i + 1])
        # if ascending:
        #     price = max(referred_price + pricetick, price)
        prices.append(price)

    lam = covered_liquidity / covered_depth
    volumes = np.random.poisson(lam, covered_depth)
    volumes = np.clip(volumes, min_volume, None)
    try:
        prices = [round_to(i, pricetick) for i in prices]
        prices = ensure_min_spacing(prices, pricetick, ascending)

        sizes = volumes / prices
        sizes = [round_to(i, lot_size) for i in sizes]

        return prices, volumes, sizes
    except:
        print(volumes / prices)


def convert_to_insert_list(order_list):
    """
    将一个 (price, qty, direction) 形式的列表转换为带有订单号的字典形式的 insert_sell_list
    :param order_list: [(price, qty, direction), ...] 格式的订单列表
    :return: [{(price, qty, direction): None}, ...] 形式的订单列表
    """
    insert_list = []
    for order in order_list:
        price, qty, direction = order
        # 使用 (price, qty, direction) 作为字典的键，None 作为值
        insert_list.append({(price, qty, direction): None})

    return insert_list


def find_next_order(order_list):
    """
    找到下一个没有订单号的订单，返回其键值对
    :param order_list: [{(price, qty, direction): order_id}, ...] 格式的订单列表
    :return: 下一个没有订单号的订单 (price, qty, direction)
    """
    for order in order_list:
        for key, value in order.items():
            if value is None:  # 如果订单号为 None，表示该订单未处理
                return key
    return None  # 如果没有未处理的订单


def update_order_id(order_list, order_key, order_id):
    """
    更新订单号
    :param order_list: [{(price, qty, direction): order_id}, ...] 格式的订单列表
    :param order_key: (price, qty, direction) 形式的订单键
    :param order_id: 要分配的订单号
    """
    for order in order_list:
        if order_key in order:
            order[order_key] = order_id
            break


def find_extreme_prices(buy_book, sell_book):
    max_buy_price = 0
    min_sell_price = 0
    if buy_book:
        # 找到 existing_buy_book 中价格的最大值
        max_buy_price = max(buy_book.values(), key=lambda x: x[0])[0]

    if sell_book:
        # 找到 existing_sell_book 中价格的最小值
        min_sell_price = min(sell_book.values(), key=lambda x: x[0])[0]

    return max_buy_price, min_sell_price


def get_str_values_from_pfit_log(pfit_log: PfitLog) -> {}:
    res = pfit_log.to_json()
    return res


class MarketMakerStrategy(CtaTemplate):
    """"""
    author = "CC Chen"

    dex_vt_symbol = ""
    refer_vt_symbol = ""

    jump_count_threshold = 0
    jump_count_threshold_on_widen = 0
    generate_count_threshold = 0
    query_open_order_threshold = 0

    kurtosis = 3
    sigma = 0.0
    miu = 0.0
    df = 0
    minimum_price = 0.1
    v_dex = 0
    v_cex = 0
    usd_book_adjust_factor = 1
    use_maker_init = False
    init_price = 0.0
    cancel_all_when_terminated = False
    max_aged_seconds = 0
    reject_orders_reset_interval = 0
    maximum_open_orders = 0
    match_threshold = 1

    alert_widen_bps = {}
    order_placement_params = []

    p_cex = 0
    p_dex = 0
    p_fit = 0.0
    paper_sell_books = []
    paper_buy_books = []

    maker_open_orders_nums = 0
    maker_reject_orders_nums = 0
    widening = False

    parameters = [
        "dex_vt_symbol", "refer_vt_symbol",
        "jump_count_threshold", "jump_count_threshold_on_widen",
        "generate_count_threshold",
        "query_open_order_threshold",
        "kurtosis", "sigma", "miu", "df", "minimum_price",
        "v_dex", "v_cex", "usd_book_adjust_factor", "use_maker_init",
        "init_price",
        "cancel_all_when_terminated",
        "max_aged_seconds",
        "reject_orders_reset_interval",
        "alert_widen_bps",
        "order_placement_params",
        "maximum_open_orders",
        "match_threshold"
    ]

    variables = ["p_fit", "maker_open_orders_nums", "maker_reject_orders_nums", "widening"]

    def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
        """"""
        super().__init__(cta_engine, strategy_name, vt_symbol, setting)

        self.pricetick = 0
        self.lot_size = 0

        # self.maximum_buy_price = 0
        # self.minimum_sell_price = 0
        self.ask1_price = 0
        self.bid1_price = 0

        self.maker_inited = False

        self.jump_count = 0
        self.clean_count = 0
        self.generate_count = 0
        self.query_open_order_count = 0
        self.reject_orders_reset_count = 0

        # Order Mgmt System Parameter
        self.pending_key_orders_cancellations = set()

        self.cancel_sell_list = []
        self.cancel_buy_list = []
        self.insert_sell_list = []
        self.insert_buy_list = []
        self.key_order_list = []

        self.pending_orders_queue = deque()
        self.batch_id = 0

        self.layer0_sell_range_extra = 0
        self.layer0_buy_range_extra = 0

    def update_key_orders(self):
        risk_buy_orders = []
        risk_sell_orders = []

        # 容错机制：如果任何一个订单簿为空
        if self.buy_orderid_order_dict and self.paper_buy_books:
            # Step 1: 识别关键买单
            for order_id, (price, quantity, direction) in self.buy_orderid_order_dict.items():
                if (price >= self.ask1_price or
                        any(price >= sell_price for sell_price, sell_qty, sell_dir in self.paper_sell_books)):
                    risk_buy_orders.append(order_id)

        if self.sell_orderid_order_dict and self.paper_sell_books:
            # Step 2: 识别关键卖单
            for order_id, (price, quantity, direction) in self.sell_orderid_order_dict.items():
                if (price <= self.bid1_price or
                        any(price <= buy_price for buy_price, buy_qty, buy_dir in self.paper_buy_books)):
                    risk_sell_orders.append(order_id)

        key_buy_orders = risk_buy_orders
        key_sell_orders = risk_sell_orders

        return key_buy_orders, key_sell_orders

    def update_om_container(self, trigger_by="ppr"):

        """
        高效生成 key order list、cancel list 和 insert list，使用集合操作优化性能
        :return: cancel_sell_list, cancel_buy_list, insert_sell_list, insert_buy_list, key_order_list
        """

        # 更新关键订单列表，防止关键订单出现在其他列表中
        key_buy_orders, key_sell_orders = self.update_key_orders()
        key_order_list = key_buy_orders + key_sell_orders

        # 提前生成订单ID和价格的集合，避免多次重复遍历
        existing_sell_orders = {order_id: price for order_id, (price, _, _) in self.sell_orderid_order_dict.items()}
        existing_buy_orders = {order_id: price for order_id, (price, _, _) in self.buy_orderid_order_dict.items()}

        paper_sell_prices = {price for price, _, _ in self.paper_sell_books}
        paper_buy_prices = {price for price, _, _ in self.paper_buy_books}

        # Step 2: 取消的卖单：在 existing_sell_orders 中但不在 paper_sell_orders 中，且不是 key order
        cancel_sell_list = [(order_id, price) for order_id, price in existing_sell_orders.items()
                            if price not in paper_sell_prices and order_id not in key_order_list]

        # 取消的买单：在 existing_buy_orders 中但不在 paper_buy_orders 中，且不是 key order
        cancel_buy_list = [(order_id, price) for order_id, price in existing_buy_orders.items()
                           if price not in paper_buy_prices and order_id not in key_order_list]

        # 插入的卖单：在 paper_sell_books 中但不在 existing_sell_orders 中
        insert_sell_list = [(price, qty, direction) for price, qty, direction in self.paper_sell_books
                            if price not in existing_sell_orders.values()]

        # 插入的买单：在 paper_buy_books 中但不在 existing_buy_orders 中
        insert_buy_list = [(price, qty, direction) for price, qty, direction in self.paper_buy_books
                           if price not in existing_buy_orders.values()]

        # Step 3: 排序取消和插入列表
        # 卖单（取消和插入）按价格从低到高排序
        self.cancel_sell_list = sorted(cancel_sell_list, key=lambda x: x[1])  # 根据价格排序

        insert_sell_list = sorted(insert_sell_list, key=lambda x: x[0])  # 根据价格排序

        self.insert_sell_list = convert_to_insert_list(insert_sell_list)

        # 买单（取消和插入）按价格从高到低排序
        self.cancel_buy_list = sorted(cancel_buy_list, key=lambda x: x[1], reverse=True)  # 价格从高到低
        insert_buy_list = sorted(insert_buy_list, key=lambda x: x[0], reverse=True)  # 价格从高到低

        self.insert_buy_list = convert_to_insert_list(insert_buy_list)

        #
        self.key_order_list = key_order_list
        self.write_log(
            f"list updated by {trigger_by}, top key = {self.key_order_list[0] if self.key_order_list else []},"
            f"top insert buy = {self.insert_buy_list[0] if self.insert_buy_list else []}, "
            f"top insert sell = {self.insert_sell_list[0] if self.insert_sell_list else []},"
            f"top cancel buy = {self.cancel_buy_list[0] if self.cancel_buy_list else []},"
            f" top cancel sell = {self.cancel_sell_list[0] if self.cancel_sell_list else []}")

        # self.write_log(f"[on_update_orders_list: {trigger_by}]: key_order_list = {self.key_order_list}, "
        #                f"cancel_sell_list = {self.cancel_sell_list}, "
        #                f"cancel_buy_list = {self.cancel_buy_list}, "
        #                f"insert_sell_list = {self.insert_sell_list}, insert_buy_list = {self.insert_buy_list}")

    def init_actions(self, trigger_by="bbo"):
        if not self.trading:
            return
        # self.write_log(f"trigger by {trigger_by} ")

        if self.key_order_list:
            # 如果有关键订单 则进入cancel 逻辑
            self.write_log(f"init action by {trigger_by}:cancel key orders")
            self.batch_cancel(self.key_order_list)
        else:
            if random.choice([True, False]):
                self.write_log(f"init_action by {trigger_by}: insert buy first")
                self.insert_next_buy_order()
                self.insert_next_sell_order()

            else:
                self.write_log(f"init_action by {trigger_by}: insert sell first")
                self.insert_next_sell_order()
                self.insert_next_buy_order()

    def generate_orderbook(self, p_fit):
        total_sell_prices = []
        total_sell_volumes = []
        total_sell_sizes = []

        total_buy_prices = []
        total_buy_volumes = []
        total_buy_sizes = []

        is_reference_ask = random.choice([True, False])

        if is_reference_ask:
            p_sell = p_fit
            p_buy = p_fit - self.pricetick

        else:
            p_buy = p_fit
            p_sell = p_fit + self.pricetick

        for param in self.order_placement_params:
            if self.widening and (param["layer"] == 0):
                sell_side_range = param["sell_side"]["range"] + self.layer0_sell_range_extra
                buy_side_range = param["buy_side"]["range"] + self.layer0_buy_range_extra
            else:
                sell_side_range = param["sell_side"]["range"]
                buy_side_range = param["buy_side"]["range"]

            # sell side
            sell_prices, sell_volumes, sell_sizes = generate_layer_orderbook(p_sell,
                                                                             p_sell * (1 + sell_side_range),
                                                                             param["sell_side"]["depth"],
                                                                             param["sell_side"]["liquidity"],
                                                                             self.pricetick,
                                                                             self.lot_size, ascending=True)
            p_sell = p_sell * (1 + sell_side_range)

            total_sell_prices.extend(sell_prices)
            total_sell_volumes.extend(sell_volumes)
            total_sell_sizes.extend(sell_sizes)

            # buy side
            buy_prices, buy_volumes, buy_sizes = generate_layer_orderbook(p_buy,
                                                                          p_buy * (1 - buy_side_range),
                                                                          param["buy_side"]["depth"],
                                                                          param["buy_side"]["liquidity"],
                                                                          self.pricetick,
                                                                          self.lot_size,
                                                                          ascending=False)

            p_buy = p_buy * (1 - buy_side_range)
            total_buy_prices.extend(buy_prices)
            total_buy_volumes.extend(buy_volumes)
            total_buy_sizes.extend(buy_sizes)

        total_sell_prices = ensure_min_spacing(total_sell_prices, self.pricetick, ascending=True)
        total_buy_prices = ensure_min_spacing(total_buy_prices, self.pricetick, ascending=False)

        paper_sell_books = [(round_to(total_sell_prices[i], self.pricetick), total_sell_sizes[i], -1) for i in
                            range(len(total_sell_prices))]
        paper_buy_books = [(round_to(total_buy_prices[i], self.pricetick), total_buy_sizes[i], 1) for i in
                           range(len(total_buy_prices))]
        self.write_log(f"pb={p_buy},topb={total_buy_prices[0]},pa={p_sell}, topa={total_sell_prices[0]}")
        # ask_prices = [i[0] for i in paper_sell_books]
        # bid_prices = [i[0] for i in paper_buy_books]
        # print(ask_prices)
        # print(bid_prices)
        return paper_sell_books, paper_buy_books

    def get_p_dex(self):
        # get dex price
        p_dex = 0
        try:
            gateway = self.cta_engine.main_engine.get_gateway(gateway_name="BBC")
            ask, bid = gateway.get_bbo(self.dex_vt_symbol.split(".")[0], self.dex_vt_symbol.split(".")[1])
            refer_price = gateway.get_trades(self.refer_vt_symbol.split(".")[0], self.refer_vt_symbol.split(".")[1])
            p_dex = (ask[0] + bid[0]) / 2 * refer_price
        except Exception as e:
            self.write_log(f"[on_p_dex]: {e}")
        finally:
            return p_dex

    def update_order_placement_range_extra(self, widen_alerts):
        """
        只调整 layer 0 的 range，确保在 alert=True 时加宽，在 alert=False 时恢复原值
        """
        self.write_log(f"收到 widen_alerts: {widen_alerts}")

        sell_range_before_update = self.order_placement_params[0]["sell_side"]["range"]
        buy_range_before_update = self.order_placement_params[0]["buy_side"]["range"]

        self.write_log(f"更新前，"
                       f"sell_range = {sell_range_before_update}, "
                       f"buy_range = {buy_range_before_update}")

        self.layer0_sell_range_extra = 0
        self.layer0_buy_range_extra = 0
        # 计算新的 widen 值
        for alert, is_active in widen_alerts.items():
            if is_active and alert in self.alert_widen_bps:
                self.layer0_buy_range_extra += self.alert_widen_bps[alert]["buy_side"]
                self.layer0_sell_range_extra += self.alert_widen_bps[alert]["sell_side"]

        self.write_log(f"更新后，"
                       f"sell_range_extra = {self.layer0_sell_range_extra}, "
                       f"buy_range_extra = {self.layer0_buy_range_extra}")

    def on_strategy_data(self, data: dict) -> None:

        if 'widen_alerts' in data.get('variables', {}):
            widen_alerts = data['variables']['widen_alerts']
            if any(widen_alerts.values()):
                self.widening = True
            else:
                self.widening = False

            self.update_order_placement_range_extra(data['variables']['widen_alerts'])
            # self.update_jump_count(data['variables']['widen_alerts'])

    def is_completed(self) -> bool:
        existing_buy_prices = {price for price, qty, direction in self.buy_orderid_order_dict.values()}
        paper_buy_prices = {price for price, qty, direction in self.paper_buy_books}

        existing_sell_prices = {price for price, qty, direction in self.sell_orderid_order_dict.values()}
        paper_sell_prices = {price for price, qty, direction in self.paper_sell_books}

        # 计算买单和卖单的匹配度
        buy_match_ratio = len(existing_buy_prices.intersection(paper_buy_prices)) / len(
            paper_buy_prices) if paper_buy_prices else 1
        sell_match_ratio = len(existing_sell_prices.intersection(paper_sell_prices)) / len(
            paper_sell_prices) if paper_sell_prices else 1
        # self.write_log(f"buy_match_ratio={buy_match_ratio}, "
        #                f"sell_match_ratio={sell_match_ratio},"
        #                f"buy_orderid_order_dict={self.buy_orderid_order_dict}, "
        #                f"sell_orderid_order_dict={self.sell_orderid_order_dict},"
        #                f"paper_buy_books={self.paper_buy_books}, "
        #                f"paper_sell_books={self.paper_sell_books}")
        # 检查是否达到匹配阈值
        self.write_log(
            msg=f"buy_ratio={round_to(buy_match_ratio, 0.01)}, sell_ratio={round_to(sell_match_ratio, 0.01)}")
        if buy_match_ratio == 0:
            msg = (f"key={self.key_order_list},"
                   f"cancel_buy={self.cancel_buy_list}, "
                   f"insert_buy={self.insert_buy_list}, "
                   f"paper_buy={self.paper_buy_books},"
                   f"existing_buy={self.buy_orderid_order_dict}")
            self.write_log(msg=msg)
        if sell_match_ratio == 0:
            msg = (f"key={self.key_order_list},"
                   f"cancel_sell={self.cancel_sell_list}, "
                   f"insert_sell={self.insert_sell_list}, "
                   f"paper_sell={self.paper_sell_books},"
                   f"existing_sell={self.sell_orderid_order_dict}")
            self.write_log(msg=msg)

        return buy_match_ratio >= self.match_threshold and sell_match_ratio >= self.match_threshold

    def is_lists_empty(self) -> bool:
        if self.key_order_list:
            return False

        if not self.insert_buy_list and not self.cancel_buy_list:
            return True

        if not self.insert_sell_list and not self.cancel_sell_list:
            return True

        return False

    def update_bbo(self):
        try:
            gateway = self.cta_engine.main_engine.get_gateway(gateway_name="BBC")
            ask_list, bid_list = gateway.get_spot_order_book(self.vt_symbol.split(".")[0], self.vt_symbol.split(".")[1])

            if not ask_list and not bid_list:

                self.ask1_price = float("inf")
                self.bid1_price = float("-inf")
            else:

                if ask_list and not bid_list:
                    self.ask1_price = ask_list[0][0]
                    self.bid1_price = self.ask1_price - self.pricetick

                if bid_list and not ask_list:
                    self.bid1_price = bid_list[0][0]
                    self.ask1_price = self.bid1_price + self.pricetick

                if ask_list and bid_list:
                    self.ask1_price = ask_list[0][0]
                    self.bid1_price = bid_list[0][0]

        except Exception as e:
            self.write_log(f"[on_update_bbo] error={e}")
            self.bid1_price = float("-inf")
            self.ask1_price = float("inf")

    def on_init(self):
        """
        Callback when strategy is inited.
        """

        self.write_log("策略初始化")
        self.parameters = self.get_parameters()
        self.write_log(f"策略初始化,当前parameters = {self.parameters}")

    def on_start(self):
        """
        Callback when strategy is started.
        """
        self.write_log("策略启动")
        # update pricetick / lot size
        self.pricetick = self.get_pricetick()
        self.lot_size = self.get_lot_size()
        self.update_bbo()
        self.write_log(f"[on_start]: update pricetick={self.pricetick}, lot_size={self.lot_size}, "
                       f"ask1={self.ask1_price}, bid1={self.bid1_price}")
        # Maker start
        if self.use_maker_init:
            self.trading = True
            self.p_fit = self.init_price
            self.paper_sell_books, self.paper_buy_books = self.generate_orderbook(self.p_fit)

            self.write_log(
                f"[on_start:maker_init] paper_buy_books={self.paper_buy_books}, paper_sell_books={self.paper_sell_books}")

            # this_batch_time = int(time.time() * 1e6)
            # this_batch_info = f"pfit={self.p_fit},p_cex={self.p_cex},time={this_batch_time}"
            # 生成关键订单
            self.insert_order_books_by_batch()

    def insert_order_books_by_batch(self):
        insert_orders_sell, cancel_orders_sell = find_differences(self.sell_orderid_order_dict, self.paper_sell_books)
        insert_orders_buy, cancel_orders_buy = find_differences(self.buy_orderid_order_dict, self.paper_buy_books)

        if insert_orders_sell:
            self.batch_insert(insert_orders_sell, order_type=OrderType.LIMIT, ref=str(self.batch_id))

        if insert_orders_buy:
            self.batch_insert(insert_orders_buy, order_type=OrderType.LIMIT, ref=str(self.batch_id))

        if cancel_orders_sell:
            self.batch_cancel(list(cancel_orders_sell.keys()))

        if cancel_orders_buy:
            self.batch_cancel(list(cancel_orders_buy.keys()))

    def on_stop(self):
        """
        Callback when strategy is stopped.
        """
        self.write_log("策略停止")
        msg = (f"[on_stop]  剩余未完成订单数量{len(self.strategy_order_trades)}, "
               f"please check {self.strategy_order_trades}")
        self.write_log(msg)
        if self.cancel_all_when_terminated:
            # self.cancel_all_orders()
            self.batch_cancel(self.strategy_order_trades)

    def on_tick(self, tick: TickData):
        """
        Callback of new tick data update.
        """
        if not tick.vt_symbol == self.vt_symbol:
            return
        # print(f"[on_tick_update] ask1_price={self.ask1_price}, bid1_price={self.bid1_price}")
        if self.ask1_price != tick.ask_price_1 or self.bid1_price != tick.bid_price_1:
            self.ask1_price = tick.ask_price_1
            self.bid1_price = tick.bid_price_1
            self.write_log(f"[on_tick_update] ask1_price={self.ask1_price}, bid1_price={self.bid1_price}")
            self.update_om_container(trigger_by="bbo")
            self.init_actions(trigger_by="bbo")

    def on_interval(self) -> None:

        self.check_order_status_count += 1
        self.clean_count += 1
        self.reject_orders_reset_count += 1
        self.generate_count += 1

        if self.querying_open_orders:
            # 如果查询中, 开启定时器
            self.query_open_order_count += 1

        if self.query_open_order_count >= self.query_open_order_threshold:
            self.write_log(f"[on_query_open_order: timeout]: need to retry")
            self.querying_open_orders = False
            self.open_orders: Dict[str, OrderTradeData] = {}
            self.query_open_order_count = 0

        if self.to_recover_queue and not self.querying_open_orders:
            # msg_detail = {}
            # for vt_orderid in self.to_recover_queue:
            #     order_trade = self.order_trades.get(vt_orderid, None)
            #     msg_detail[vt_orderid] = order_trade

            msg = f"action:query_open_orders"
            self.write_log(msg)
            symbol, exchange = extract_vt_symbol(self.vt_symbol)
            req = QueryOpenOrderRequest(symbol=symbol, exchange=Exchange.BTSE, product=Product.SPOT)
            self.cta_engine.query_open_orders(req)
            self.querying_open_orders = True

        if self.reject_orders_reset_count >= self.reject_orders_reset_interval:
            if self.maker_reject_orders_nums != 0:
                self.maker_reject_orders_nums = 0

        if self.check_order_status_count >= self.check_order_status_interval:
            self.check_order_status()
            self.check_order_status_count = 0

        if self.clean_count >= self.max_aged_seconds:
            self.clean_expired_order_trades(self.max_aged_seconds)

        if self.generate_count >= self.generate_count_threshold:
            self.update_MM_flow(trigger_by="tm")
            self.generate_count = 0

    def calculate_p_weight(self):

        if not self.v_dex + self.v_cex:  # 避免分母为0的情况
            return None
        w_cex = self.v_cex / (self.v_dex + self.v_cex)
        w_dex = 1 - w_cex
        print(f"p_cex={self.p_cex}, p_dex={self.p_dex}")
        return self.p_cex * w_cex + self.p_dex * w_dex

    def calculate_jump_value(self):
        """计算并返回跳跃值"""
        jump_count_threshold = self.jump_count_threshold if not self.widening else self.jump_count_threshold_on_widen
        if self.jump_count >= jump_count_threshold:
            self.jump_count = 0
            return self.kurtosis * t.rvs(self.df, loc=self.miu, scale=self.sigma)
        else:
            self.jump_count += 1
            return 0

    def on_public_trade(self, public_trade: PublicTradeData):
        # print(f"[on_public_trade]:{public_trade}")
        if not self.trading or self.wait:
            return

        # 更新 p cex
        self.p_cex = public_trade.price
        if not self.p_cex:
            self.write_log(f"取不到p_cex {public_trade.price}")
            return

        # 更新 p dex
        p_dex = self.get_p_dex()
        if not p_dex:
            self.write_log("取不到p_dex")
            return

        if self.p_dex != p_dex:
            self.p_dex = p_dex

        # self.write_log("")
        # self.write_log(f"is list empty {self.is_lists_empty()}")

        if self.is_completed():
            self.update_MM_flow(trigger_by="ls")

        elif self.is_lists_empty():
            self.update_MM_flow(trigger_by="le")

    def update_MM_flow(self, trigger_by="td"):
        if len(self.strategy_order_trades) >= self.maximum_open_orders:
            self.write_log("超过最大挂单数")
            return

        # 计算加权价格和跳跃值
        p_weight = self.calculate_p_weight()

        random_value = self.calculate_jump_value()

        # 计算最终价格并四舍五入
        p_fit = round_to(p_weight + random_value, self.pricetick)

        if self.p_fit != p_fit:
            self.batch_id += 1
            pfit_log = PfitLog(trigger_by=trigger_by,
                               last_p_fit=self.p_fit, new_p_fit=p_fit,
                               p_weight=p_weight, jump_value=random_value,
                               jump_count=self.jump_count, p_cex=self.p_cex,
                               p_dex=self.p_dex,
                               ask1_price=self.ask1_price,
                               bid1_price=self.bid1_price,
                               kurtosis=self.kurtosis, sigma=self.sigma,
                               miu=self.miu, df=self.df,
                               v_dex=self.v_dex, v_cex=self.v_cex)

            self.p_fit = p_fit
            self.maker_open_orders_nums = len(self.strategy_order_trades)
            self.put_event()

            self.paper_sell_books, self.paper_buy_books = self.generate_orderbook(self.p_fit)
            self.update_om_container(trigger_by="ppr")
            self.init_actions(trigger_by="ppr")

            # write csv
            pfit_log.paper_top_buy = self.paper_buy_books[0][0]
            pfit_log.paper_top_sell = self.paper_sell_books[0][0]
            pfit_log.maker_open_order_nums = self.maker_open_orders_nums
            msg = get_str_values_from_pfit_log(pfit_log)

            self.write_log(f"[to_csv]{msg}")
            # spread = pfit_log.paper_top_sell - pfit_log.paper_top_buy

    def update_trade_stats(self, order_trade, trade_volume):
        direction = order_trade.direction
        trade_cost = trade_volume * order_trade.trade_price

        if direction == Direction.LONG:
            # print("on_order_trade", order_trade, trade_volume * order_trade.trade_price)
            self.symbol_traded[order_trade.direction] += trade_volume
            self.symbol_traded_cost[order_trade.direction] += trade_cost
            # print("on_order_trade", self.symbol_traded_cost[order_trade.direction])
        else:
            self.symbol_traded[order_trade.direction] -= trade_volume
            self.symbol_traded_cost[order_trade.direction] -= trade_cost

    def cleanup_finished_order(self, order_trade):
        vt_orderid = order_trade.vt_orderid
        if vt_orderid in self.strategy_order_trades:
            self.strategy_order_trades.remove(vt_orderid)

        if vt_orderid in self.to_recover_queue:
            self.to_recover_queue.remove(vt_orderid)

        order_dict = (self.buy_orderid_order_dict if order_trade.direction == Direction.LONG
                      else self.sell_orderid_order_dict)

        if vt_orderid in order_dict:
            try:
                del order_dict[vt_orderid]
                # self.write_log(f"{vt_orderid} removed from {'buy' if order_trade.direction == Direction.LONG else
                # 'sell'} order dict")
            except Exception:
                msg = f"{order_trade.direction.value} order to be removed [{vt_orderid}], " \
                      f"{(order_trade.price, order_trade.volume, 1 if order_trade.direction == Direction.LONG else -1)}, " \
                      f"existing_{'buy' if order_trade.direction == Direction.LONG else 'sell'}_dict: {order_dict}"
                self.write_log(msg)

    def on_order_trade(self, order_trade: OrderTradeData):
        """
        Manage Order Status：
        self.order_trades
        self.buy_order_orderid_dict
        self.sell_order_orderid_dict
        """

        trade_volume = order_trade.trade_diff
        latest_order_trade = self.order_trades.get(order_trade.vt_orderid, None)

        if latest_order_trade:
            # 过滤逆序的cancelling -> not traded
            # if latest_order_trade.status == Status.CANCELLING and order_trade.status == Status.NOTTRADED:
            #     return
            order_trade.cancel_num = latest_order_trade.cancel_num
            order_trade.query_num = latest_order_trade.query_num
            order_trade.latest_cancel_time = latest_order_trade.latest_cancel_time
            order_trade.latest_query_time = latest_order_trade.latest_query_time
            if not order_trade.is_exchange_status():
                order_trade.exchange_status = latest_order_trade.exchange_status
            else:
                order_trade.exchange_status = order_trade.status

        self.order_trades[order_trade.vt_orderid] = order_trade

        if trade_volume:
            msg = "ORDER FILLED[{}]，{}，{}，{}@{},ref={}".format(
                order_trade.vt_orderid,
                order_trade.vt_symbol,
                order_trade.direction.value,
                order_trade.trade_diff,
                order_trade.trade_price,
                order_trade.reference
            )
            self.write_log(msg)
            self.update_trade_stats(order_trade, trade_volume)

        msg = ("ORDER {}[{}], ref={} source={},query_num={}, cancel_num={}, latest_query_time={}, "
               "latest_cancel_time={}").format(
            order_trade.status.value,
            order_trade.vt_orderid,
            order_trade.reference,
            order_trade.api_source,
            order_trade.query_num,
            order_trade.cancel_num,
            order_trade.latest_query_time,
            order_trade.latest_cancel_time
        )
        self.write_log(msg)

        if order_trade.is_finished():
            self.cleanup_finished_order(order_trade)
            if order_trade.status == Status.REJECTED:
                rejection_msg = "委托{}[{}]{}{}".format(
                    order_trade.status.value,
                    order_trade.vt_orderid,
                    order_trade.error_id,
                    order_trade.error_msg
                )
                self.write_log(rejection_msg)
                # 有拒单发送消息
                if order_trade.error_id != -1:
                    self.send_dingding(rejection_msg)
                    self.maker_reject_orders_nums += 1

        self.on_om_order_trade(order_trade)

    def on_om_order_trade(self, order_trade: OrderTradeData):
        """
        驱动后续逻辑,理论上只接收not traded和cancelled
        """
        if not self.trading:
            return

        if order_trade.is_finished():
            self.update_om_container(trigger_by="od")
            self.init_actions(trigger_by="od")

        direction = order_trade.direction
        status = order_trade.status

        if status == Status.NOTTRADED and direction == Direction.LONG:
            self.cancel_next_buy_order()

        elif status == Status.CANCELLED and direction == Direction.LONG:
            self.insert_next_buy_order()

        elif status == Status.NOTTRADED and direction == Direction.SHORT:
            self.cancel_next_sell_order()

        elif status == Status.CANCELLED and direction == Direction.SHORT:
            self.insert_next_sell_order()

        # if not self.key_order_list:
        #     self.process_pending_orders()

    def handle_list_size_mismatch(self, empty_list: EmptyList):
        if empty_list == EmptyList.INSERT_BUY:
            if self.cancel_buy_list:
                self.write_log(f"action: batch cancel buy")
                cancel_list = [item[0] for item in self.cancel_buy_list]
                self.batch_cancel(cancel_list)

        elif empty_list == EmptyList.CANCEL_BUY:
            if self.insert_buy_list:
                self.write_log(f"action: batch insert buy")
                for order in self.insert_buy_list:
                    (price, qty, direction), order_id = next(iter(order.items()))
                    if order_id is None:
                        [new_order_id] = self.send_order(Direction.LONG, price, qty, OrderType.LIMIT,
                                                         ref=str(self.batch_id))
                        order[(price, qty, direction)] = new_order_id

        elif empty_list == EmptyList.INSERT_SELL:
            if self.cancel_sell_list:
                self.write_log(f"action: batch cancel sell")
                cancel_list = [item[0] for item in self.cancel_sell_list]
                self.batch_cancel(cancel_list)

        elif empty_list == EmptyList.CANCEL_SELL:
            if self.insert_sell_list:
                self.write_log(f"action: batch insert sell")
                for order in self.insert_sell_list:
                    (price, qty, direction), order_id = next(iter(order.items()))
                    if order_id is None:
                        [new_order_id] = self.send_order(Direction.SHORT, price, qty, OrderType.LIMIT,
                                                         ref=str(self.batch_id))
                        order[(price, qty, direction)] = new_order_id

    def insert_next_buy_order(self):
        self.write_log("action: insert buy")
        if not self.insert_buy_list:
            self.write_log("Insert Buy List is empty.")
            self.handle_list_size_mismatch(empty_list=EmptyList.INSERT_BUY)
            return

        # 取出第一个订单的价格和数量
        first_order = self.insert_buy_list[0]
        (price, qty, direction), order_id = list(first_order.items())[0]

        if order_id is None:
            """
            说明没下过单
            """
            [new_order_id] = self.send_order(Direction.LONG, price, qty, OrderType.LIMIT, ref=str(self.batch_id))
            self.insert_buy_list[0] = {(price, qty, direction): new_order_id}

        else:
            order_trade = self.order_trades.get(order_id, None)
            if order_trade is None or order_trade.is_in_flight():
                self.write_log(f"Order {order_id} is still in-flight.")
                # self.write_log(f"Order {order_id} is still in-flight. Adding to pending orders queue.")
                # self.pending_orders_queue.append(("insert_next_buy", self.insert_next_buy_order))

    def insert_next_sell_order(self):
        self.write_log("action: insert sell")
        if not self.insert_sell_list:
            self.write_log("Insert Sell List is empty.")
            self.handle_list_size_mismatch(empty_list=EmptyList.INSERT_SELL)
            return

        # 取出第一个订单的价格和数量
        first_order = self.insert_sell_list[0]
        (price, qty, direction), order_id = list(first_order.items())[0]

        if order_id is None:
            [new_order_id] = self.send_order(Direction.SHORT, price, qty, OrderType.LIMIT, ref=str(self.batch_id))
            self.insert_sell_list[0] = {(price, qty, direction): new_order_id}

        else:
            order_trade = self.order_trades.get(order_id, None)

            if order_trade is None or order_trade.is_pending():
                self.write_log(f"Order {order_id} is still in-flight.")
                # self.write_log(f"Order {order_id} is still in-flight. Adding to pending orders queue.")
                # self.pending_orders_queue.append(("insert_next_sell", self.insert_next_sell_order))

    def cancel_next_buy_order(self):
        self.write_log("action: cancel buy")
        if not self.cancel_buy_list:
            self.write_log("Cancel Buy List is empty")
            self.handle_list_size_mismatch(empty_list=EmptyList.CANCEL_BUY)
            return

        first_order = self.cancel_buy_list[0]
        order_id, price = first_order

        order_trade = self.order_trades.get(order_id, None)

        if order_trade is None or not order_trade.is_cancellable():
            status = order_trade.status if order_trade else "-"
            self.write_log(f"Order {status}[{order_id}] is still in-flight")

        else:
            self.batch_cancel([order_id], checked_status=True)

    def cancel_next_sell_order(self):
        self.write_log("action: cancel sell")
        if not self.cancel_sell_list:
            self.write_log("Cancel Sell List is empty")
            self.handle_list_size_mismatch(empty_list=EmptyList.CANCEL_SELL)
            return

        first_order = self.cancel_sell_list[0]
        order_id, price = first_order

        order_trade = self.order_trades.get(order_id, None)

        if order_trade is None or not order_trade.is_cancellable():
            status = order_trade.status if order_trade else "-"
            self.write_log(f"Order {status}[{order_id}] is still in-flight")
            # self.write_log(f"Order {order_id} is still in-flight. Adding to pending orders queue.")
            # self.pending_orders_queue.append(("cancel_next_sell", self.cancel_next_sell_order))
        else:
            self.batch_cancel([order_id], checked_status=True)

    # def process_pending_orders(self):
    #     """
    #     处理队列中的pending订单
    #     """
    #     if self.pending_orders_queue:
    #         next_order = self.pending_orders_queue.popleft()
    #         exec_type, func = next_order
    #         self.write_log(f"处理pending订单: {exec_type}")
    #         func()  # 调用对应的插单或撤单函数
